﻿// -----------------------------------------------------------------------
// <copyright company="Christoph van der Fecht - VDsoft">
// Copyright (C) Christoph van der Fecht - VDsoft - All Rights Reserved
// Unauthorized copying of this file, via any medium is strictly prohibited,
// unless it is communicated and authorized by the copyright owner.
// </copyright>
// -----------------------------------------------------------------------

namespace SeriesManager.DataLayer
{
	using SeriesManager.Model;
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;
	using System.Text.RegularExpressions;

	/// <summary>
	/// Handels all file access
	/// </summary>
	public sealed class FileManager
	{
		/// <summary>
		/// Default instance of the <see cref="FileManager"/> class.
		/// </summary>
		private static readonly FileManager DefaultInstance = new FileManager();

		private static Regex alreadyRenamedExpresseion = new Regex(@".*\s-\sS\d+E\d+\s-\s.*", RegexOptions.Compiled);

		private static Regex extractorPatter = new Regex(@"((S|s)\d+(E|e)\d+)|(\d+×\d+)", RegexOptions.Compiled | RegexOptions.CultureInvariant);

		/// <summary>
		/// Prevents a default instance of the <see cref="Filemanager"/> class from being created.
		/// </summary>
		private FileManager()
		{ 
		}

		/// <summary>
		/// Gets the default instance of the <see cref="FileManager"/> class.
		/// </summary>
		public static FileManager Instance
		{
			get
			{
				return DefaultInstance;
			}
		}

		/// <summary>
		/// Renames all episodes of a series that are availabe at the series path.
		/// </summary>
		/// <param name="series"><see cref="SeriesManager.Model.Series"/> object that contains all details of the series.</param>
		/// <param name="seriesPath">Path where all files are stored.</param>
		public void RenameSeries(Series series, string seriesPath)
		{
			if (series == null)
			{
				throw new ArgumentNullException("series", "Provided series must not be null.");
			}

			// get all files inside the directory(s)
			List<FileInfo> files = this.GetFiles(seriesPath);

			// pattern to rename the episode
			string renameFormat = "{0} - S{1}E{2} - {3}{4}";

			List<string> failedFiles = new List<string>();

			foreach (FileInfo currentFile in files)
			{
				int seasonNumber = 0;
				int episodeNumber = 0;

				this.ExtractSeassonAndEpisode(currentFile.Name, out seasonNumber, out episodeNumber);

				Season currentSeason = series.Seasons.SingleOrDefault(x => x.Number == seasonNumber);

				if (currentSeason == null)
				{
					continue;
				}

				Episode currentEpisode = currentSeason.Episodes.SingleOrDefault(x => x.Number == episodeNumber);

				if (currentEpisode == null)
				{
					continue;
				}

				string episodeName = string.Empty;

				if (currentEpisode.Name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
				{
					episodeName = this.PrepareFileName(currentEpisode.Name);
				}
				else
				{
					episodeName = currentEpisode.Name;
				}


				string dir = currentFile.Directory.FullName;
				string newPath = Path.Combine(dir, string.Format(renameFormat, series.Name, seasonNumber.ToString("D2"), episodeNumber.ToString("D2"), episodeName, currentFile.Extension));

				try
				{
					currentFile.MoveTo(newPath);
				}
				catch (IOException)
				{
					failedFiles.Add(currentFile.Name);
				}
			}

			if (failedFiles.Count > 0)
			{
				string message = "The following files weren't renamed:\n";
				foreach (string failedFile in failedFiles)
				{
					message += string.Concat(failedFile, "\n");
				}

				System.Windows.Forms.MessageBox.Show(message, "Renaming error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning);
			}
		}

		/// <summary>
		/// Extracts the season and episode number of the provided filename.
		/// </summary>
		/// <param name="name">Name f the file that contains season and episode number.</param>
		/// <param name="seasonNumber">Season number.</param>
		/// <param name="episodeNumber">Episode number.</param>
		private void ExtractSeassonAndEpisode(string name, out int seasonNumber, out int episodeNumber)
		{
			if (string.IsNullOrEmpty(name))
			{
				seasonNumber = 0;
				episodeNumber = 0;
				return;
			}

			string extracted = extractorPatter.Match(name).Groups[0].Value;

			// means that the file hasn't the right format.
			if (extracted.Count() < 6)
			{
				seasonNumber = 0;
				episodeNumber = 0;
				return;
			}

			string seasonString = extracted.Substring(1, extracted.LastIndexOf("e", StringComparison.OrdinalIgnoreCase)-1);
			string episodeString = extracted.Substring(extracted.IndexOf(seasonString)+3,2);

			int.TryParse(seasonString, out seasonNumber);
			int.TryParse(episodeString, out episodeNumber);
		}

		/// <summary>
		/// Gets all files inside the provided path.
		/// </summary>
		/// <param name="seriesPath">Path of where all episodes of the series are stored.</param>
		/// <returns>Collection of all files that are not already renamed correctly.</returns>
		private List<FileInfo> GetFiles(string seriesPath)
		{
			if (string.IsNullOrEmpty(seriesPath))
			{
				throw new ArgumentNullException("seriesPath", "Path to the series must not be null or empty.");
			}

			DirectoryInfo directory = new DirectoryInfo(seriesPath);

			if (!directory.Exists)
			{
				throw new ArgumentNullException("seriesPath", "Provided directory doesn't exists.");
			}

			List<FileInfo> result = directory.GetFiles("*.*", SearchOption.AllDirectories)
				.Where(x => x.Extension != ".jpg" && x.Extension != "*.png" && x.Extension != ".db" && !alreadyRenamedExpresseion.IsMatch(x.Name))
				.ToList();
			return result;
		}

		/// <summary>
		/// Checks if the provided name contains any invalid characters for a filename.
		/// </summary>
		/// <param name="toCheck">Value to check.</param>
		/// <returns>Valid filename</returns>
		private string PrepareFileName(string toCheck)
		{
			if (toCheck.Contains("\\"))
			{
				toCheck = toCheck.Replace("\\", " ");
			}

			if (toCheck.Contains("/"))
			{
				toCheck = toCheck.Replace("/", " ");
			}

			if (toCheck.Contains(":"))
			{
				toCheck = toCheck.Replace(":", " ");
			}

			if (toCheck.Contains("?"))
			{
				toCheck = toCheck.Replace("?", " ");
			}

			if (toCheck.Contains("\""))
			{
				toCheck = toCheck.Replace("\"", " ");
			}

			if (toCheck.Contains("<"))
			{
				toCheck = toCheck.Replace("<", " ");
			}

			if (toCheck.Contains(">"))
			{
				toCheck = toCheck.Replace(">", " ");
			}

			if (toCheck.Contains("|"))
			{
				toCheck = toCheck.Replace("|", " ");
			}

			return toCheck.Trim();
		}
	}
}
